using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;

namespace MosziNET.Web.Gallery.Providers
{

    public enum Relation
    {
        Previous,
        Next,
        First,
        Last
    }

    /// <summary>
    /// Provides the means to work with the images 
    /// </summary>
    public abstract class GalleryStorageProvider
    {
        private Dictionary<string, Folder[]> cachedFolderLists;
        private Dictionary<string, Folder> cachedFolders;
        private Dictionary<string, FolderImage[]> cachedImageLists;

        #region / Abstract members /

        public abstract string ConnectionString { get; set; }

        public abstract byte[] GetCachedThumbnail(string thumbnailId);
        public abstract void CacheThumbnail(string thumbnailId, byte[] content);
        public abstract Stream GetImage(FolderImage image);
        
        protected abstract Folder[] GetFolderListInternal(string parentFolderId);
        protected abstract Folder GetFolderInternal(string folderId);
        protected abstract FolderImage[] GetImageListInternal(string parentFolderId);

        #endregion / Abstract members /

        #region / Constructors /

        /// <summary>
        /// Creates a new instance of the GalleryStorageProvider
        /// </summary>
        public GalleryStorageProvider()
        {
            this.cachedFolderLists = new Dictionary<string, Folder[]>();
            this.cachedFolders = new Dictionary<string, Folder>();
            this.cachedImageLists = new Dictionary<string, FolderImage[]>();
        }

        #endregion / Constructors /

        #region / Public methods /

        /// <summary>
        /// Returns the sub folder list of the specified parent folder
        /// </summary>
        /// <param name="parentFolderId"></param>
        /// <returns></returns>
        public Folder[] GetFolderList(string parentFolderId)
        {
            if (String.IsNullOrEmpty(parentFolderId))
                parentFolderId = "0";

            if (!cachedFolderLists.ContainsKey(parentFolderId))
            {
                lock (cachedFolderLists)
                {
                    if (!cachedFolderLists.ContainsKey(parentFolderId))
                    {
                        cachedFolderLists.Add(parentFolderId,
                            GetFolderListInternal(parentFolderId));
                    }
                }
            }

            return cachedFolderLists[parentFolderId];
        }

        /// <summary>
        /// Returns the sub folder list of the specified parent folder
        /// </summary>
        /// <param name="parentFolderId"></param>
        /// <returns></returns>
        public FolderImage[] GetImageList(string parentFolderId)
        {
            if (String.IsNullOrEmpty(parentFolderId))
                throw new ArgumentNullException("parentFolderId");

            if (!cachedImageLists.ContainsKey(parentFolderId))
            {
                lock (cachedImageLists)
                {
                    if (!cachedImageLists.ContainsKey(parentFolderId))
                    {
                        cachedImageLists.Add(parentFolderId,
                            GetImageListInternal(parentFolderId));
                    }
                }
            }

            return cachedImageLists[parentFolderId];
        }

        /// <summary>
        /// Returns the image from the same folder as the specified image, 
        /// and with the relation specified
        /// </summary>
        /// <param name="image"></param>
        /// <param name="relation"></param>
        /// <returns></returns>
        public FolderImage GetRelatedImage(FolderImage image, Relation relation)
        {
            if (image == null || String.IsNullOrEmpty(image.ParentFolder) ||
                String.IsNullOrEmpty(image.ImageName))
                throw new ArgumentException("image");

            List<FolderImage> imageList = new List<FolderImage>(GetImageList(image.ParentFolder));
            FolderImage relatedImage = null;
            int index;
            switch (relation)
            {
                case Relation.First:
                    relatedImage = imageList[0];
                    break;
                case Relation.Previous:
                    index = FindFolderImage(imageList, image);
                    if (index > 0)
                        relatedImage = imageList[index - 1];
                    break;
                case Relation.Next:
                    index = FindFolderImage(imageList, image);
                    if (index < imageList.Count - 1)
                        relatedImage = imageList[index + 1]; 
                    break;
                case Relation.Last:
                    relatedImage = imageList[imageList.Count - 1];
                    break;
            }

            return relatedImage;
        }

        /// <summary>
        /// Returns the folder based on the folder id
        /// </summary>
        /// <param name="folderId"></param>
        /// <returns></returns>
        public Folder GetFolder(string folderId)
        {
            if (!cachedFolders.ContainsKey(folderId))
            {
                lock (cachedFolders)
                {
                    if (!cachedFolders.ContainsKey(folderId))
                    {
                        cachedFolders.Add(folderId, GetFolderInternal(folderId));
                    }
                }
            }

            return cachedFolders[folderId];
        }

        #endregion / Public methods /

        #region / Helper methods /

        private int FindFolderImage(List<FolderImage> images, FolderImage image)
        {
            for (int i = 0; i < images.Count; i++)
            {
                if (images[i].ParentFolder == image.ParentFolder &&
                    images[i].ImageName == image.ImageName)
                    return i;
            }

            return -1;
        }

        #endregion / Helper methods /
    }
}
